Skip to content

Unbundle #165: hide Resend when sign-in can't recover + demo cookie outlasts OTP wait#187

Merged
aspiers merged 12 commits into
mainfrom
unbundle/otp-resend-par-liveness
Jul 14, 2026
Merged

Unbundle #165: hide Resend when sign-in can't recover + demo cookie outlasts OTP wait#187
aspiers merged 12 commits into
mainfrom
unbundle/otp-resend-par-liveness

Conversation

@aspiers

@aspiers aspiers commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Unbundles the Tier 1 OTP sign-in resiliency work from #165 into a focused, reviewable branch. The PR covers three user-visible failure modes in two logical changesets:

  1. Reconcile the existing heartbeat after interruption. The email-code screen already refreshed the upstream authorization request every three minutes before this PR. This change records when a heartbeat last succeeded and checks immediately when a backgrounded tab returns to the foreground, so the UI promptly reflects a request that expired while timers or networking were suspended.

  2. Stop offering actions that cannot succeed. If the upstream authorization request or local auth flow has expired, Resend code is hidden and a single Start over action is shown. This avoids sending a fresh code that can no longer complete the OAuth flow.

  3. Keep the demo client's callback state for the whole recoverable window. The demo's OAuth-state cookie previously expired after 10 minutes, with its timer starting before the OTP was issued. It now lasts one hour, matching the auth-flow lifetime. If that cookie is genuinely missing, the callback shows an honest “sign-in took too long” message instead of generic “Authentication failed” copy; a present but invalid cookie remains an authentication failure.

The heartbeat-liveness tracking and unrecoverable-flow UI are grouped in one changeset because they are complementary halves of the same behavior: use the existing heartbeat's result while recovery is possible, then stop offering Resend once it is not. The demo cookie/error handling has its own changeset because it affects a separate component and failure boundary.

Manual testing

Use the PR's Railway environment, either demo client, and an email inbox that can receive OTP messages. These scenarios use ordinary user/browser interactions; no test hooks, console commands, or developer-tools cookie editing are required.

1. A sign-in that really died is reconciled on return

  1. Start an email sign-in and reach the OTP page.
  2. Prevent heartbeats for more than five minutes using a realistic interruption: disconnect the device from the network, put the laptop to sleep, lock a phone with the browser backgrounded, or suspend the browser tab.
  3. Restore connectivity/wake the device and return to the OTP tab. If it was already foregrounded, switch away and back once.

Expected: returning to the tab triggers an immediate liveness check. Resend code becomes absent and exactly one Start over action is visible, without waiting up to three minutes for the next interval tick. Clicking it returns to the client cleanly; beginning a new sign-in then works normally.

This distinguishes the PR from earlier deployments: they already had the periodic heartbeat, but did not immediately reconcile on visibilitychange or hide Resend based on the age of the last successful heartbeat.

2. A short network interruption does not falsely kill the flow

  1. Start an email sign-in and reach the OTP page.
  2. Disconnect the device for less than five minutes, then reconnect and return to the tab.
  3. Click Resend code or submit the original code while it is still valid.

Expected: the immediate check succeeds, Resend code remains available, and the sign-in can complete. A transient network failure must not be mistaken for a definitively expired authorization request.

3. A resent code works after the old demo-cookie boundary

  1. Start an email sign-in and remain on the OTP page for about 8–9 minutes, leaving the tab active enough for its existing heartbeat to run.
  2. Click Resend code and use the new code.
  3. Submit it after more than 10 minutes have elapsed since the sign-in originally started, but within 10 minutes of the resend.

Expected: sign-in completes. This verifies that the demo's OAuth-state cookie no longer disappears at the original 10-minute boundary. The heartbeat itself is pre-existing behavior and is only a prerequisite for keeping this long test flow recoverable.

4. Missing demo callback state gets the honest error

  1. Start an email sign-in and reach the OTP page.
  2. While leaving that auth-service page open, use the browser's normal privacy/site-data settings to remove cookies for the demo client origin only. Do not clear the auth-service origin, since its cookie is needed to finish OTP verification.
  3. Return to the OTP page and submit the still-valid code.

Expected: the demo redirects to its sign-in page with “Your sign-in took too long to finish. Please sign in again.” It must not show the generic “Authentication failed” message.

Realistic alternatives: use a browser profile or privacy extension configured to clear site data for the demo origin when it is closed. This models users who selectively clear or automatically purge client-site cookies.

Automated test plan

  • Rebased onto current origin/main in a fresh worktree.
  • pnpm format:check, pnpm lint, and pnpm typecheck pass.
  • pnpm test passes: 1,045 tests across 71 files.
  • pnpm test:coverage passes the ratcheted thresholds.
  • E2E scenarios pass, including @resend-hidden-when-par-dead and @demo-cookie-expiry.
  • All PR CI, deployment, security, and analysis checks pass.

Relationship to #165

Carves the OTP/PAR liveness and demo callback-state fixes out of #165. The remaining OTP-UX adjacencies, accessibility polish, error-copy rewrites, handle-picker fixes, and Account Settings flash banners are independent follow-ups.

Summary by CodeRabbit

  • New Features
    • Email code sign-in sessions stay usable for up to one hour.
    • Added Start over when the flow can’t be completed (instead of only failing at the end).
  • Bug Fixes
    • Improved messaging: expired sign-in now shows a session expired prompt to sign in again (replacing generic “Authentication failed”).
    • Resend code visibility now updates based on liveness, including after returning to the tab.
  • Tests
    • Expanded end-to-end and unit coverage for session-expiry and PAR/OTP recovery UX.

@changeset-bot

changeset-bot Bot commented Jul 6, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 647475b

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Jul 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
epds-demo Ready Ready Preview, Comment Jul 13, 2026 3:37pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR extends OTP heartbeat handling with PAR-expiry recovery, increases the demo OAuth state cookie lifetime to one hour, distinguishes expired OAuth sessions from authentication failures, and adds E2E coverage for both recovery paths.

Changes

Authentication session recovery

Layer / File(s) Summary
PAR liveness and OTP recovery
packages/auth-service/src/routes/login-page.ts, packages/auth-service/src/__tests__/login-page.test.ts, .changeset/keep-sign-in-alive-and-recover.md
OTP heartbeat state is tracked across visibility changes; expired PAR sessions hide resend actions and expose a Start over path.
Demo OAuth session expiry handling
packages/demo/src/lib/session.ts, packages/demo/src/app/api/oauth/..., packages/demo/src/app/components/LoginForm.tsx, packages/demo/src/__tests__/session.test.ts, .changeset/demo-cookie-outlasts-otp-wait.md
The OAuth state cookie lasts one hour, missing cookies resolve to session_expired, and the demo renders dedicated timeout messaging with unit coverage.
Expiry and recovery E2E coverage
e2e/step-definitions/auth.steps.ts, features/passwordless-authentication.feature, vitest.config.ts
End-to-end scenarios verify PAR-dead Start over behavior and expired demo OAuth session redirects; coverage thresholds are updated.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant OTPPage
  participant AuthService
  participant DemoClient
  User->>OTPPage: Enter emailed code
  OTPPage->>AuthService: Check PAR heartbeat
  AuthService-->>OTPPage: Return PAR liveness
  OTPPage->>DemoClient: Submit OAuth callback
  DemoClient-->>User: Redirect with session_expired or auth_failed
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the two main changes: OTP recovery now hides Resend, and the demo cookie lifetime was extended to outlast OTP wait.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch unbundle/otp-resend-par-liveness

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coveralls-official

coveralls-official Bot commented Jul 6, 2026

Copy link
Copy Markdown

Coverage Report for CI Build 29262960040

Coverage increased (+0.1%) to 56.327%

Details

  • Coverage increased (+0.1%) from the base build.
  • Patch coverage: 5 uncovered changes across 1 file (6 of 11 lines covered, 54.55%).
  • No coverage regressions found.

Uncovered Changes

File Changed Covered %
packages/demo/src/app/api/oauth/callback/route.ts 5 0 0.0%
Total (2 files) 11 6 54.55%

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 2973
Covered Lines: 1665
Line Coverage: 56.0%
Relevant Branches: 1840
Covered Branches: 1046
Branch Coverage: 56.85%
Branches in Coverage %: Yes
Coverage Strength: 5.89 hits per line

💛 - Coveralls

@railway-app

railway-app Bot commented Jul 6, 2026

Copy link
Copy Markdown

🚅 Deployed to the ePDS-pr-187 environment in ePDS

Service Status Web Updated (UTC)
@certified-app/auth-service ✅ Success (View Logs) Web Jul 13, 2026 at 3:38 pm
@certified-app/demo ✅ Success (View Logs) Web Jul 13, 2026 at 3:32 pm
@certified-app/demo untrusted ✅ Success (View Logs) Web Jul 13, 2026 at 3:32 pm
@certified-app/pds-core ✅ Success (View Logs) Web Jul 13, 2026 at 12:09 pm

aspiers and others added 2 commits July 12, 2026 21:11
Previously the OTP screen always offered "Resend code", even when the
upstream PAR row had silently lapsed (suspended tab, mobile background,
heartbeat throttling). The user could click Resend, receive a fresh
email, type the new code, and only then see "Sign in failed" — wasting
their time on a code that could not have worked.

The screen now never surfaces actions that cannot complete the flow:

- Track lastSuccessfulHeartbeatAt; treat the PAR as dead once we cross
  upstream's 5 min AUTHORIZATION_INACTIVITY_TIMEOUT without a fresh ok
  ping (the upstream death point is exact — no margin needed).
- Hide #btn-resend and surface a #btn-start-over (→ /auth/abort) the
  moment parLikelyDead() flips. Reconciled on every heartbeat tick
  (including transient ticks, so a stale-by-time case still hides the
  button) and on the visibilitychange event (so a backgrounded tab
  returning to focus reflects reality immediately).
- Inline "Send a new code" action on the OTP-expired error now branches:
  parLikelyDead() → "Start over"; otherwise existing "Send a new code".

This is the proactive UI complement to the existing reactive abort gate.
Server-side enforcement of the same invariant on /email-otp/send-
verification-otp and /sign-in/email-otp is a separate follow-up.

Test: new @resend-hidden-when-par-dead scenario; full
@otp-and-par-expiry / @par-heartbeat / @resend-after-par-dead /
@otp-expiry suite still passes (7 scenarios, 78 steps).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…doesn't

Two changes that close the bug-report hole on the demo client:

1. Bump oauth_state cookie maxAge from 600s to 3600s, matching
   auth-service's auth_flow row TTL. The demo's cookie carries the
   state, code verifier, token endpoint and issuer for the OAuth
   callback to complete; if it expires before the user submits the
   OTP, the callback can't find any of that and bounces silently.
   600s was shorter than realistic OTP-form sit times (the bug
   report was an 11-minute wait). Aligning with auth_flow's 60-min
   budget means as long as the auth-service can still recover the
   flow, the demo can too.

2. Distinguish "cookie missing" from "auth failed" on the callback.
   Previously every silent-fail path bounced to /?error=auth_failed
   ("Authentication failed. Please try again."), which is misleading
   when the sign-in itself succeeded — the user typed a fresh OTP
   correctly, the auth-service issued the OAuth code, the demo just
   couldn't finish because its own session cookie had aged out. Now
   the cookie-missing branch redirects to /?error=session_expired
   ("Your sign-in took too long to finish. Please sign in again.")
   so the user understands what happened without thinking they
   typed the code wrong.

Test: new @demo-cookie-expiry @bug-report scenario clears the cookie
mid-flow and asserts the session-expired error surfaces.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Expand the sign-in timeout release note to cover both keeping active code-entry flows alive and recovering clearly when a flow has expired. Rename the changeset to match the broader scope.

Co-authored-by: Codex <noreply@openai.com>
@aspiers
aspiers marked this pull request as ready for review July 12, 2026 21:49
Copilot AI review requested due to automatic review settings July 12, 2026 21:49

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR unbundles the Tier-1 OTP sign-in resiliency fixes from #165, ensuring users aren’t offered “Resend code” actions that cannot succeed after upstream PAR expiry, and extending the demo client’s OAuth-state cookie lifetime so it outlasts realistic email-OTP wait times while surfacing a more accurate session_expired error when state is missing.

Changes:

  • Auth-service login page now proactively hides Resend code (replacing it with Start over) when the PAR is likely dead, reconciling on heartbeat ticks and visibilitychange.
  • Demo client increases oauth_state cookie maxAge to 1 hour and introduces session_expired messaging for missing OAuth state in the callback.
  • Adds/updates E2E coverage for resend-visibility behavior and demo cookie expiry behavior; adds changesets for both slices.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packages/demo/src/lib/session.ts Adds OAUTH_COOKIE_MAX_AGE_SECONDS and a helper for resolving callback error codes.
packages/demo/src/app/components/LoginForm.tsx Adds user-facing copy for the new session_expired error.
packages/demo/src/app/api/oauth/login/route.ts Uses the shared max-age constant when setting the OAuth-state cookie.
packages/demo/src/app/api/oauth/callback/route.ts Redirects to session_expired when the signed OAuth-state cookie is missing.
packages/demo/src/tests/session.test.ts Unit tests for the cookie lifetime constant and error-code mapping.
packages/auth-service/src/routes/login-page.ts Implements parLikelyDead() and toggles Resend vs Start over based on PAR liveness.
features/passwordless-authentication.feature Adds scenarios for resend-button hiding and demo cookie expiry behavior.
e2e/step-definitions/auth.steps.ts Adds Playwright steps to force PAR re-check and simulate demo cookie expiry.
.changeset/keep-sign-in-alive-and-recover.md Changeset for the auth-service OTP-screen behavior.
.changeset/demo-cookie-outlasts-otp-wait.md Changeset for the demo cookie lifetime + callback error surfacing.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/demo/src/app/api/oauth/callback/route.ts Outdated
Comment thread e2e/step-definitions/auth.steps.ts Outdated
Comment thread .changeset/demo-cookie-outlasts-otp-wait.md Outdated
Comment thread .changeset/keep-sign-in-alive-and-recover.md Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/demo/src/app/api/oauth/callback/route.ts (1)

52-67: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Rename the inner code variable to avoid shadowing the OAuth authorization code.

Line 39 declares const code (the authorization code from the query string), and line 65 redeclares const code inside the if (!stateData) block. Although the block returns immediately so there's no functional defect, the shadowing makes the code harder to read and is a footgun for future edits.

♻️ Proposed rename
     if (!stateData) {
       console.error('[oauth/callback] Missing oauth_state cookie')
-      const code = resolveCallbackErrorCode({ oauthCookiePresent: false })
-      return NextResponse.redirect(new URL(`/?error=${code}`, baseUrl))
+      const errorCode = resolveCallbackErrorCode({ oauthCookiePresent: false })
+      return NextResponse.redirect(new URL(`/?error=${errorCode}`, baseUrl))
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/demo/src/app/api/oauth/callback/route.ts` around lines 52 - 67,
Rename the block-scoped `code` variable in the `if (!stateData)` branch of the
OAuth callback to a distinct error-code name, preserving the existing
`resolveCallbackErrorCode` result and redirect behavior while avoiding shadowing
the authorization `code` declared earlier.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.changeset/demo-cookie-outlasts-otp-wait.md:
- Line 11: Update the changeset paragraph to remove the claim that
token-endpoint invalid_grant failures redirect to session_expired. State only
that a missing or expired oauth_state cookie redirects to session_expired, while
token-endpoint failures continue redirecting to auth_failed; retain the cookie
maxAge and user-facing message details.

In `@e2e/step-definitions/auth.steps.ts`:
- Around line 898-906: Update the stale cookie-expiry comments surrounding the
programmatic cookie deletion step to reference the new one-hour lifetime,
including replacing the 10-minute timing examples and describing the callback
outcome as redirecting with session_expired instead of auth_failed. Keep the
explanation of why the cookie is deleted programmatically unchanged.

---

Nitpick comments:
In `@packages/demo/src/app/api/oauth/callback/route.ts`:
- Around line 52-67: Rename the block-scoped `code` variable in the `if
(!stateData)` branch of the OAuth callback to a distinct error-code name,
preserving the existing `resolveCallbackErrorCode` result and redirect behavior
while avoiding shadowing the authorization `code` declared earlier.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 62d26a85-449e-483d-9c9a-5fcab2cfef47

📥 Commits

Reviewing files that changed from the base of the PR and between 4fd3230 and e1c9d06.

📒 Files selected for processing (10)
  • .changeset/demo-cookie-outlasts-otp-wait.md
  • .changeset/keep-sign-in-alive-and-recover.md
  • e2e/step-definitions/auth.steps.ts
  • features/passwordless-authentication.feature
  • packages/auth-service/src/routes/login-page.ts
  • packages/demo/src/__tests__/session.test.ts
  • packages/demo/src/app/api/oauth/callback/route.ts
  • packages/demo/src/app/api/oauth/login/route.ts
  • packages/demo/src/app/components/LoginForm.tsx
  • packages/demo/src/lib/session.ts

Comment thread .changeset/demo-cookie-outlasts-otp-wait.md Outdated
Comment thread e2e/step-definitions/auth.steps.ts Outdated
Correct the timeout release notes and E2E commentary, avoid shadowing the authorization code, and use a boolean cookie-presence check.

Co-authored-by: Codex <noreply@openai.com>
Copilot AI review requested due to automatic review settings July 13, 2026 12:08
@railway-app
railway-app Bot temporarily deployed to ePDS / ePDS-pr-187 July 13, 2026 12:08 Destroyed
@aspiers

aspiers commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

(reply generated by Codex)

Addressed the remaining actionable summary and top-level findings in bf7c45a:

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.

Comment thread e2e/step-definitions/auth.steps.ts
Comment thread .changeset/demo-cookie-outlasts-otp-wait.md Outdated
Apply the final review wording correction to the demo cookie changeset.

Co-authored-by: Codex <noreply@openai.com>
Copilot AI review requested due to automatic review settings July 13, 2026 14:59
@railway-app
railway-app Bot temporarily deployed to ePDS / ePDS-pr-187 July 13, 2026 15:00 Destroyed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.

@aspiers

aspiers commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

Final head is ready for review. (request posted by Codex)

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

@aspiers Got it — I'll review the latest head now.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@aspiers

aspiers commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

Please review the final green head ecc91fb. (request posted by Codex)

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

@aspiers Got it — I'll review the final head at ecc91fb4873ff0af4ff44cd6bd4b42dff95d271f now.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copilot AI review requested due to automatic review settings July 13, 2026 15:29
@railway-app
railway-app Bot temporarily deployed to ePDS / ePDS-pr-187 July 13, 2026 15:29 Destroyed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 1 comment.

Comment thread packages/auth-service/src/routes/login-page.ts Outdated
@aspiers

aspiers commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

(reply generated by Codex)

Addressed the CodeRabbit docstring-coverage summary warning in 4d12548: the two newly introduced production helpers that lacked JSDoc (parLikelyDead and readOAuthSessionCookie) now document their contract and classification behavior. The remaining functions counted by the heuristic are existing/anonymous browser callbacks and test helpers; adding boilerplate docstrings to those would conflict with this repository's comment guidance. Full format, lint, typecheck, 1,045 tests, coverage, and E2E all pass on this head.

Copilot AI review requested due to automatic review settings July 13, 2026 15:36
@railway-app
railway-app Bot temporarily deployed to ePDS / ePDS-pr-187 July 13, 2026 15:36 Destroyed
@sonarqubecloud

Copy link
Copy Markdown

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.

@aspiers

aspiers commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

Please review final head 647475b when the review window opens. (request posted by Codex)

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

@aspiers Got it — I'll review the final head at 647475b1951fb5af45f27209ee2b6bce29bf61db now.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@aspiers

aspiers commented Jul 14, 2026

Copy link
Copy Markdown
Contributor Author

Saw this from testing with faked network disconnect then reconnect ~6m later:

image

@aspiers
aspiers merged commit 4f52652 into main Jul 14, 2026
24 checks passed
@aspiers
aspiers deleted the unbundle/otp-resend-par-liveness branch July 14, 2026 12:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants